Here we will map an example dataset from Roy et al (Cell Rep 2019) comprising 12,245 CD34+ cells from fetal and adult bone marrow. Note that any human hematopoietic cells, regardless of tissue source or disease status, can be mapped to this reference. This tutorial will take approximately 10 minutes to run.

Setup

library(Seurat)
library(tidyverse)
library(symphony)
library(ggpubr)
library(patchwork)

Install package from github

## install dependencies that are not on CRAN
if(!require(BiocManager, quietly = TRUE)) install.packages("BiocManager")
BiocManager::install(c("AUCell", "doMC"))
if(!require(devtools, quietly = TRUE)) install.packages("devtools")
devtools::install_github("jaredhuling/jcolors")
## install BoneMarrowMap package
devtools::install_github('andygxzeng/BoneMarrowMap', force = TRUE)
Downloading GitHub repo andygxzeng/BoneMarrowMap@HEAD

HEMATOPOIESIS - Download reference object and UMAP model

Set projection folder, Download reference object and UMAP model

# Set directory to store projection reference files
projection_path = './'

# Download Bone Marrow Reference - 344 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrow_RefMap_SymphonyRef.rds', 
                    destfile = paste0(projection_path, 'BoneMarrow_RefMap_SymphonyRef.rds'))
# Download uwot model file - 221 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrow_RefMap_uwot_model.uwot', 
                    destfile = paste0(projection_path, 'BoneMarrow_RefMap_uwot_model.uwot'))

# Load Symphony reference
ref <- readRDS(paste0(projection_path, 'BoneMarrow_RefMap_SymphonyRef.rds'))
# Set uwot path for UMAP projection
ref$save_uwot_path <- paste0(projection_path, 'BoneMarrow_RefMap_uwot_model.uwot')

Visualize Bone Marrow Reference

If we want to visualize celltype labels or metadata from the BM Reference, we can create a Seurat Object from the symphony reference This will be memory efficient as it will not include gene expression counts, only the UMAP coordinates and the metadata including cell labels and sorting information

BM_ref_obj <- create_ReferenceObject(BM_ref)

DimPlot(BM_ref_obj, reduction = 'umap', group.by = 'CellType_Annotation_formatted', raster=FALSE, label=TRUE, label.size = 4)

We can visualize other annotations too, including cell cycle phase and lineage pseudotime estimates.

p1 <- DimPlot(BM_ref_obj, reduction = 'umap', group.by = 'CyclePhase', raster=FALSE)
p2 <- FeaturePlot(BM_ref_obj, reduction = 'umap', features = 'Pseudotime', raster=FALSE) 

p1 + p2

Load Leukemia scRNA-seq data.

Let’s use these maps to project scRNA-seq data from two Ph+ B-ALL patients with distinct cell compositions (SJPHALL006_D, SJPHALL007_D)

Each patient sample was downsampled to 2500 cells to decrease runtime for this example.

query <- readRDS('ExampleQuery_BALL_scRNAseq.rds')
query
An object of class Seurat 
36601 features across 13265 samples within 1 assay 
Active assay: RNA (36601 features, 0 variable features)

Map the Query Data

Provide raw counts, metadata, and donor key. This should take <1 min Calculate mapping error and perform QC to remove low quality cells with high mapping error

# batch variable to correct in the query data, set as NULL if no batches in query
batchvar <- 'Sample'

# Map query dataset using Symphony (Kang et al 2021)
query <- map_Query(
    exp_query = query@assays$RNA@counts, 
    metadata_query = query@meta.data,
    ref_obj = BM_ref,
    vars = batchvar
)
Normalizing
Scaling and synchronizing query gene expression
Found 2360 reference variable genes in query dataset
Project query cells using reference gene loadings
Clustering query cells to reference centroids
Correcting query batch effects
UMAP
All done!
Warning: Adding features not currently present in the objectWarning: Invalid name supplied, making object name syntactically valid. New object name is Seurat..ProjectDim.RNA.harmony; see ?make.names for more details on syntax validity

In leukemia samples, the distribution of mapping error scores can vary broadly from sample to sample. In this context, we will want to threshold outliers with high mapping error on a per-sample basis. Typically, a threshold of 2, 2.5, or 3 MADs works well.

In some cases where sequencing depth is very low (e.g. older datasets from first-generation scRNA-seq protocols), a more stringent threshold of even 1.5 may be warranted to eliminate cells with low mapping quality

# Run QC based on mapping error score, flag cells with mapping error >= 2.5 MADs above median
query <- query %>% calculate_MappingError(., reference = BM_ref, MAD_threshold = 2.5, 
                                          threshold_by_donor = TRUE, donor_key = batchvar) # threshold mapping error on a per-sample basis.

# Plot distribution by patient to ensure you are catching the tail
query@meta.data %>% 
  ggplot(aes(x = mapping_error_score, fill = mapping_error_QC)) + 
  geom_histogram(bins = 200) + facet_wrap(.~get(batchvar))

# Get QC Plots
QC_plots <- plot_MappingErrorQC(query)

# Plot together - If this is too crowded, can also just call "QC_plots" aloneto display one by one
patchwork::wrap_plots(QC_plots, ncol = 4, widths = c(0.8, 0.3, 0.8, 0.3))

This important step identifies a subset of cells with high mapping error from the query dataset that are either:

Sometimes, low quality cells may erroneously map to the orthochromatic erythroblast region as this cell type has very low transcriptional diversity. These low quality query cells do not have hemoglobin expression and are in fact mis-mapped; they will be flagged by the QC filter and excluded from cell type assignments.

Please adjust the MAD_threshold (typically between 1 and 3) based on the distribution of your dataset to identify the outliers with low quality and high mapping error scores. This will improve your classifications and any downstream composition analysis

# # Optional step - remove outliers with high mapping error
# query <- subset(query, mapping_error_QC == 'Pass')

Optionally, outlier cells with high mapping error can also be removed at this stage. For ease of integrating these mapped annotations with the rest of your analysis, we can choose to skip this step. If so, Final CellType and Pseudotime predictions will be assigned as NA for cells failing the mapping error QC threshold.

Cell Type Assignments

We will next use a KNN classifier to assign cell identity based on the 30 K-Nearest Neighbours from the reference map. This label transfer step will take longer, potentially around 10 minutes for ~10,000 cells

# Predict Hematopoietic Cell Types by KNN classification
query <- predict_CellTypes(
  query_obj = query, 
  ref_obj = BM_ref, 
  initial_label = 'initial_CellType_BoneMarrowMap', # celltype assignments before filtering on mapping QC
  final_label = 'predicted_CellType_BoneMarrowMap'  # celltype assignments with map QC failing cells assigned as NA
) 

DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap', group.by = c('predicted_CellType_BoneMarrowMap'), 
        raster=FALSE, label=TRUE, label.size = 4)

Pseudotime Annotations

We can also annotate each query cell based on their position along hematopoietic pseudotime. Query cells will be assigned a pseudotime score based on the 30 K-Nearest Neighbours from the reference map. Since our Pseudotime KNN assignments are performed in UMAP space (more accurate than KNN on harmony components), this step is very fast (< 10s)

library(RColorBrewer)
# Predict Pseudotime values by KNN
query <- predict_Pseudotime(
  query_obj = query, 
  ref_obj = BM_ref, 
  initial_label = 'initial_Pseudotime',  # pseudotime assignments before filtering on mapping QC
  final_label = 'predicted_Pseudotime'   # pseudotime assignments with map QC failing cells assigned as NA
)

# Visualize Hematopoietic Pseudotime in query data
FeaturePlot(subset(query, mapping_error_QC == 'Pass'), features = c('predicted_Pseudotime'), split.by = 'Sample') & 
  scale_color_gradientn(colors = rev(brewer.pal(11, 'RdBu')))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Visualize Projection Density

Now let’s visualize the density distribution of query cells across the hematopoietic hierarchy

# Set batch/condition to be visualized individually
batch_key <- 'Sample'

# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
  query_obj = query, 
  batch_key = batch_key, 
  ref_obj = BM_ref, 
  Hierarchy_only = FALSE, # Whether to exclude T/NK/Plasma/Stromal cells 
  downsample_reference = TRUE, 
  downsample_frac = 0.25,   # down-sample reference cells to 25%; reduces figure file size
  query_point_size = 0.2,   # adjust size of query cells based on # of cells
  saveplot = TRUE, 
  save_folder = 'projectionFigures/'
)

# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 2)

We can also set Hierarchy_only = TRUE to remove T/NK/Plasma/Stromal cells and focus solely on the hematopoietic hierarchy.

# Set batch/condition to be visualized individually
batch_key <- 'Sample'

# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
  query_obj = query, 
  batch_key = batch_key, 
  ref_obj = BM_ref, 
  Hierarchy_only = TRUE, # Whether to exclude T/NK/Plasma/Stromal cells 
  downsample_reference = TRUE, 
  downsample_frac = 0.25,   # down-sample reference cells to 25%; reduces figure file size
  query_point_size = 0.2,   # adjust size of query cells based on # of cells
  saveplot = TRUE, 
  save_folder = 'projectionFigures/'
)

# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 2)

Let’s save the results of the mapping onto the complete hematopoietic hierarchy.

saveRDS(query, 'QueryData_Mapped_CompleteHematopoiesis.rds')

B CELL DEVELOPMENT - Download reference object and UMAP model

Set projection folder, Download reference object and UMAP model

# Set directory to store projection reference files
projection_path = './'

# # Download Bone Marrow Reference - 187 Mb
# curl::curl_download('https://bdevelopmentmap.s3.us-east-2.amazonaws.com/BDevelopment_RefMap_SymphonyRef.rds', 
#                     destfile = paste0(projection_path, 'BDevelopment_RefMap_SymphonyRef.rds'))
# # Download uwot model file - 99 Mb
# curl::curl_download('https://bdevelopmentmap.s3.us-east-2.amazonaws.com/BDevelopment_RefMap_uwot_model.uwot', 
#                     destfile = paste0(projection_path, 'BDevelopment_RefMap_uwot_model.uwot'))

# Load Symphony reference
bdev_ref <- readRDS(paste0(projection_path, 'BDevelopment_RefMap_SymphonyRef.rds'))
bdev_ref$umap$embedding[,1] = -bdev_ref$umap$embedding[,1] 

# Set uwot path for UMAP projection
bdev_ref$save_uwot_path <- paste0(projection_path, 'BDevelopment_RefMap_uwot_model.uwot')

Visualize Bone Marrow Reference

If we want to visualize celltype labels or metadata from the BM Reference, we can create a Seurat Object from the symphony reference This will be memory efficient as it will not include gene expression counts, only the UMAP coordinates and the metadata including cell labels and sorting information

bdev_ref_obj <- create_ReferenceObject(bdev_ref)

DimPlot(bdev_ref_obj, reduction = 'umap', group.by = 'BDevelopment_CellType', raster=FALSE, label=TRUE, label.size = 4)

Mapping query data onto B cell development

query <- readRDS('QueryData_Mapped_CompleteHematopoiesis.rds')
query
An object of class Seurat 
36601 features across 13265 samples within 1 assay 
Active assay: RNA (36601 features, 0 variable features)
 3 dimensional reductions calculated: pca, harmony, umap

Filter for celltypes along B cell development

B_development_celltypes <- c('HSC', 'HSC/MPP', 'MPP-MyLy', 'MPP-LMPP', 'LMPP', 'Early GMP', 'MLP', 'MLP-II', 'Pre-pDC', 'Pre-pDC Cycling', 'pDC', 
                    'CLP', 'EarlyProB', 'Pre-ProB', 'Pro-B VDJ', 'Pro-B Cycling', 'Large Pre-B', 'Small Pre-B', 'Immature B', 'Mature B')

query <- subset(query, predicted_CellType_BoneMarrowMap %in% B_development_celltypes)
query
An object of class Seurat 
36601 features across 11572 samples within 1 assay 
Active assay: RNA (36601 features, 0 variable features)
 3 dimensional reductions calculated: pca, harmony, umap

Map the Query Data

Provide raw counts, metadata, and donor key. This should take <1 min Calculate mapping error and perform QC to remove low quality cells with high mapping error


# Map query dataset using Symphony (Kang et al 2021)
query <- map_Query(
    exp_query = query@assays$RNA@counts, 
    metadata_query = query@meta.data,
    ref_obj = bdev_ref,
    vars = batchvar
)
Normalizing
Scaling and synchronizing query gene expression
Found 950 reference variable genes in query dataset
Project query cells using reference gene loadings
Clustering query cells to reference centroids
Correcting query batch effects
UMAP
All done!
Warning: Invalid name supplied, making object name syntactically valid. New object name is Seurat..ProjectDim.RNA.harmony; see ?make.names for more details on syntax validity
# Flip UMAP1 to go from left to right
query[['umap']]@cell.embeddings[,1] = -query[['umap']]@cell.embeddings[,1]
query[['umap']]
bdev_ref$meta_data

Cell Type Assignments

We will next use a KNN classifier to assign cell identity based on the 30 K-Nearest Neighbours from the reference map. This label transfer step will take longer, potentially around 10 minutes for ~10,000 cells

# Predict Hematopoietic Cell Types by KNN classification
query <- predict_CellTypes(
  query_obj = query, 
  ref_obj = bdev_ref, 
  ref_label = 'BDevelopment_CellType',   ## for a more detailed annotation, use BDevelopment_CellType_Comprehensive
  initial_label = 'initial_CellType_BDevelopment', # celltype assignments before filtering on mapping QC
  final_label = 'predicted_CellType_BDevelopment'  # celltype assignments with map QC failing cells assigned as NA
) 

DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap', group.by = c('predicted_CellType_BDevelopment'), 
        raster=FALSE, label=TRUE, label.size = 4)

Visualize Projection Density

Now let’s visualize the density distribution of query cells across the hematopoietic hierarchy

saveRDS(query, 'QueryData_Mapped_BcellDevelopment.rds')
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSGVyZSB3ZSB3aWxsIG1hcCBhbiBleGFtcGxlIGRhdGFzZXQgZnJvbSBSb3kgZXQgYWwgKENlbGwgUmVwIDIwMTkpIGNvbXByaXNpbmcgMTIsMjQ1IENEMzQrIGNlbGxzIGZyb20gZmV0YWwgYW5kIGFkdWx0IGJvbmUgbWFycm93LgpOb3RlIHRoYXQgYW55IGh1bWFuIGhlbWF0b3BvaWV0aWMgY2VsbHMsIHJlZ2FyZGxlc3Mgb2YgdGlzc3VlIHNvdXJjZSBvciBkaXNlYXNlIHN0YXR1cywgY2FuIGJlIG1hcHBlZCB0byB0aGlzIHJlZmVyZW5jZS4KVGhpcyB0dXRvcmlhbCB3aWxsIHRha2UgYXBwcm94aW1hdGVseSAxMCBtaW51dGVzIHRvIHJ1bi4KCiMjIyBTZXR1cAoKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzeW1waG9ueSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkocGF0Y2h3b3JrKQpgYGAKCkluc3RhbGwgcGFja2FnZSBmcm9tIGdpdGh1YgoKYGBge3J9CiMjIGluc3RhbGwgZGVwZW5kZW5jaWVzIHRoYXQgYXJlIG5vdCBvbiBDUkFOCmlmKCFyZXF1aXJlKEJpb2NNYW5hZ2VyLCBxdWlldGx5ID0gVFJVRSkpIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKQmlvY01hbmFnZXI6Omluc3RhbGwoYygiQVVDZWxsIiwgImRvTUMiKSkKaWYoIXJlcXVpcmUoZGV2dG9vbHMsIHF1aWV0bHkgPSBUUlVFKSkgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImphcmVkaHVsaW5nL2pjb2xvcnMiKQpgYGAKIApgYGB7cn0KIyMgaW5zdGFsbCBCb25lTWFycm93TWFwIHBhY2thZ2UKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdhbmR5Z3h6ZW5nL0JvbmVNYXJyb3dNYXAnLCBmb3JjZSA9IFRSVUUpCmxpYnJhcnkoQm9uZU1hcnJvd01hcCkKYGBgCgogCiMjIyMgSEVNQVRPUE9JRVNJUyAtIERvd25sb2FkIHJlZmVyZW5jZSBvYmplY3QgYW5kIFVNQVAgbW9kZWwKClNldCBwcm9qZWN0aW9uIGZvbGRlciwgRG93bmxvYWQgcmVmZXJlbmNlIG9iamVjdCBhbmQgVU1BUCBtb2RlbAoKYGBge3J9CiMgU2V0IGRpcmVjdG9yeSB0byBzdG9yZSBwcm9qZWN0aW9uIHJlZmVyZW5jZSBmaWxlcwpwcm9qZWN0aW9uX3BhdGggPSAnLi8nCgojIERvd25sb2FkIEJvbmUgTWFycm93IFJlZmVyZW5jZSAtIDM0NCBNYgpjdXJsOjpjdXJsX2Rvd25sb2FkKCdodHRwczovL2JvbmVtYXJyb3dtYXAuczMudXMtZWFzdC0yLmFtYXpvbmF3cy5jb20vQm9uZU1hcnJvd19SZWZNYXBfU3ltcGhvbnlSZWYucmRzJywgCiAgICAgICAgICAgICAgICAgICAgZGVzdGZpbGUgPSBwYXN0ZTAocHJvamVjdGlvbl9wYXRoLCAnQm9uZU1hcnJvd19SZWZNYXBfU3ltcGhvbnlSZWYucmRzJykpCiMgRG93bmxvYWQgdXdvdCBtb2RlbCBmaWxlIC0gMjIxIE1iCmN1cmw6OmN1cmxfZG93bmxvYWQoJ2h0dHBzOi8vYm9uZW1hcnJvd21hcC5zMy51cy1lYXN0LTIuYW1hem9uYXdzLmNvbS9Cb25lTWFycm93X1JlZk1hcF91d290X21vZGVsLnV3b3QnLCAKICAgICAgICAgICAgICAgICAgICBkZXN0ZmlsZSA9IHBhc3RlMChwcm9qZWN0aW9uX3BhdGgsICdCb25lTWFycm93X1JlZk1hcF91d290X21vZGVsLnV3b3QnKSkKCiMgTG9hZCBTeW1waG9ueSByZWZlcmVuY2UKQk1fcmVmIDwtIHJlYWRSRFMocGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JvbmVNYXJyb3dfUmVmTWFwX1N5bXBob255UmVmLnJkcycpKQojIFNldCB1d290IHBhdGggZm9yIFVNQVAgcHJvamVjdGlvbgpCTV9yZWYkc2F2ZV91d290X3BhdGggPC0gcGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JvbmVNYXJyb3dfUmVmTWFwX3V3b3RfbW9kZWwudXdvdCcpCmBgYAoKCiMjIyMgVmlzdWFsaXplIEJvbmUgTWFycm93IFJlZmVyZW5jZQoKSWYgd2Ugd2FudCB0byB2aXN1YWxpemUgY2VsbHR5cGUgbGFiZWxzIG9yIG1ldGFkYXRhIGZyb20gdGhlIEJNIFJlZmVyZW5jZSwgd2UgY2FuIGNyZWF0ZSBhIFNldXJhdCBPYmplY3QgZnJvbSB0aGUgc3ltcGhvbnkgcmVmZXJlbmNlIApUaGlzIHdpbGwgYmUgbWVtb3J5IGVmZmljaWVudCBhcyBpdCB3aWxsIG5vdCBpbmNsdWRlIGdlbmUgZXhwcmVzc2lvbiBjb3VudHMsIG9ubHkgdGhlIFVNQVAgY29vcmRpbmF0ZXMgYW5kIHRoZSBtZXRhZGF0YSBpbmNsdWRpbmcgY2VsbCBsYWJlbHMgYW5kIHNvcnRpbmcgaW5mb3JtYXRpb24KCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMX0KQk1fcmVmX29iaiA8LSBjcmVhdGVfUmVmZXJlbmNlT2JqZWN0KEJNX3JlZikKCkRpbVBsb3QoQk1fcmVmX29iaiwgcmVkdWN0aW9uID0gJ3VtYXAnLCBncm91cC5ieSA9ICdDZWxsVHlwZV9Bbm5vdGF0aW9uX2Zvcm1hdHRlZCcsIHJhc3Rlcj1GQUxTRSwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZSA9IDQpCmBgYAoKV2UgY2FuIHZpc3VhbGl6ZSBvdGhlciBhbm5vdGF0aW9ucyB0b28sIGluY2x1ZGluZyBjZWxsIGN5Y2xlIHBoYXNlIGFuZCBsaW5lYWdlIHBzZXVkb3RpbWUgZXN0aW1hdGVzLgoKYGBge3IsIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9MTF9CnAxIDwtIERpbVBsb3QoQk1fcmVmX29iaiwgcmVkdWN0aW9uID0gJ3VtYXAnLCBncm91cC5ieSA9ICdDeWNsZVBoYXNlJywgcmFzdGVyPUZBTFNFKQpwMiA8LSBGZWF0dXJlUGxvdChCTV9yZWZfb2JqLCByZWR1Y3Rpb24gPSAndW1hcCcsIGZlYXR1cmVzID0gJ1BzZXVkb3RpbWUnLCByYXN0ZXI9RkFMU0UpIAoKcDEgKyBwMgpgYGAKCgojIyMjIExvYWQgTGV1a2VtaWEgc2NSTkEtc2VxIGRhdGEuCgpMZXQncyB1c2UgdGhlc2UgbWFwcyB0byBwcm9qZWN0IHNjUk5BLXNlcSBkYXRhIGZyb20gdHdvIFBoKyBCLUFMTCBwYXRpZW50cyB3aXRoIGRpc3RpbmN0IGNlbGwgY29tcG9zaXRpb25zIChTSlBIQUxMMDA2X0QsIFNKUEhBTEwwMDdfRCkKCkVhY2ggcGF0aWVudCBzYW1wbGUgd2FzIGRvd25zYW1wbGVkIHRvIDI1MDAgY2VsbHMgdG8gZGVjcmVhc2UgcnVudGltZSBmb3IgdGhpcyBleGFtcGxlLiAKCmBgYHtyfQpjdXJsOjpjdXJsX2Rvd25sb2FkKCdodHRwczovL2JkZXZlbG9wbWVudG1hcC5zMy51cy1lYXN0LTIuYW1hem9uYXdzLmNvbS9FeGFtcGxlUXVlcnlfQkFMTF9zY1JOQXNlcS5yZHMnLCAKICAgICAgICAgICAgICAgICAgICBkZXN0ZmlsZSA9IHBhc3RlMChwcm9qZWN0aW9uX3BhdGgsICdFeGFtcGxlUXVlcnlfQkFMTF9zY1JOQXNlcS5yZHMnKSkKCnF1ZXJ5IDwtIHJlYWRSRFMoJ0V4YW1wbGVRdWVyeV9CQUxMX3NjUk5Bc2VxLnJkcycpCnF1ZXJ5CmBgYAoKCiMjIyBNYXAgdGhlIFF1ZXJ5IERhdGEKUHJvdmlkZSByYXcgY291bnRzLCBtZXRhZGF0YSwgYW5kIGRvbm9yIGtleS4gVGhpcyBzaG91bGQgdGFrZSA8MSBtaW4KQ2FsY3VsYXRlIG1hcHBpbmcgZXJyb3IgYW5kIHBlcmZvcm0gUUMgdG8gcmVtb3ZlIGxvdyBxdWFsaXR5IGNlbGxzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yCgpgYGB7cn0KIyBiYXRjaCB2YXJpYWJsZSB0byBjb3JyZWN0IGluIHRoZSBxdWVyeSBkYXRhLCBzZXQgYXMgTlVMTCBpZiBubyBiYXRjaGVzIGluIHF1ZXJ5CmJhdGNodmFyIDwtICdTYW1wbGUnCgojIE1hcCBxdWVyeSBkYXRhc2V0IHVzaW5nIFN5bXBob255IChLYW5nIGV0IGFsIDIwMjEpCnF1ZXJ5IDwtIG1hcF9RdWVyeSgKICAgIGV4cF9xdWVyeSA9IHF1ZXJ5QGFzc2F5cyRSTkFAY291bnRzLCAKICAgIG1ldGFkYXRhX3F1ZXJ5ID0gcXVlcnlAbWV0YS5kYXRhLAogICAgcmVmX29iaiA9IEJNX3JlZiwKICAgIHZhcnMgPSBiYXRjaHZhcgopCmBgYAoKSW4gbGV1a2VtaWEgc2FtcGxlcywgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtYXBwaW5nIGVycm9yIHNjb3JlcyBjYW4gdmFyeSBicm9hZGx5IGZyb20gc2FtcGxlIHRvIHNhbXBsZS4gSW4gdGhpcyBjb250ZXh0LCB3ZSB3aWxsIHdhbnQgdG8gdGhyZXNob2xkIG91dGxpZXJzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yIG9uIGEgcGVyLXNhbXBsZSBiYXNpcy4gVHlwaWNhbGx5LCBhIHRocmVzaG9sZCBvZiAyLCAyLjUsIG9yIDMgTUFEcyB3b3JrcyB3ZWxsLiAKCkluIHNvbWUgY2FzZXMgd2hlcmUgc2VxdWVuY2luZyBkZXB0aCBpcyB2ZXJ5IGxvdyAoZS5nLiBvbGRlciBkYXRhc2V0cyBmcm9tIGZpcnN0LWdlbmVyYXRpb24gc2NSTkEtc2VxIHByb3RvY29scyksIGEgbW9yZSBzdHJpbmdlbnQgdGhyZXNob2xkIG9mIGV2ZW4gMS41IG1heSBiZSB3YXJyYW50ZWQgdG8gZWxpbWluYXRlIGNlbGxzIHdpdGggbG93IG1hcHBpbmcgcXVhbGl0eSAKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KIyBSdW4gUUMgYmFzZWQgb24gbWFwcGluZyBlcnJvciBzY29yZSwgZmxhZyBjZWxscyB3aXRoIG1hcHBpbmcgZXJyb3IgPj0gMi41IE1BRHMgYWJvdmUgbWVkaWFuCnF1ZXJ5IDwtIHF1ZXJ5ICU+JSBjYWxjdWxhdGVfTWFwcGluZ0Vycm9yKC4sIHJlZmVyZW5jZSA9IEJNX3JlZiwgTUFEX3RocmVzaG9sZCA9IDIuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZF9ieV9kb25vciA9IFRSVUUsIGRvbm9yX2tleSA9IGJhdGNodmFyKSAjIHRocmVzaG9sZCBtYXBwaW5nIGVycm9yIG9uIGEgcGVyLXNhbXBsZSBiYXNpcy4KCiMgUGxvdCBkaXN0cmlidXRpb24gYnkgcGF0aWVudCB0byBlbnN1cmUgeW91IGFyZSBjYXRjaGluZyB0aGUgdGFpbApxdWVyeUBtZXRhLmRhdGEgJT4lIAogIGdncGxvdChhZXMoeCA9IG1hcHBpbmdfZXJyb3Jfc2NvcmUsIGZpbGwgPSBtYXBwaW5nX2Vycm9yX1FDKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAwKSArIGZhY2V0X3dyYXAoLn5nZXQoYmF0Y2h2YXIpKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MTB9CiMgR2V0IFFDIFBsb3RzClFDX3Bsb3RzIDwtIHBsb3RfTWFwcGluZ0Vycm9yUUMocXVlcnkpCgojIFBsb3QgdG9nZXRoZXIgLSBJZiB0aGlzIGlzIHRvbyBjcm93ZGVkLCBjYW4gYWxzbyBqdXN0IGNhbGwgIlFDX3Bsb3RzIiBhbG9uZXRvIGRpc3BsYXkgb25lIGJ5IG9uZQpwYXRjaHdvcms6OndyYXBfcGxvdHMoUUNfcGxvdHMsIG5jb2wgPSA0LCB3aWR0aHMgPSBjKDAuOCwgMC4zLCAwLjgsIDAuMykpCmBgYAoKClRoaXMgaW1wb3J0YW50IHN0ZXAgaWRlbnRpZmllcyBhIHN1YnNldCBvZiBjZWxscyB3aXRoIGhpZ2ggbWFwcGluZyBlcnJvciBmcm9tIHRoZSBxdWVyeSBkYXRhc2V0IHRoYXQgYXJlIGVpdGhlcjoKCiogbm90IHByZXNlbnQgd2l0aGluIHRoZSByZWZlcmVuY2UsIG9yCiogaGF2ZSBwb29yIFFDIG1ldHJpY3MgKGxvdyBSTkEgY291bnRzIGFuZCBsb3cgdHJhbnNjcmlwdGlvbmFsIGRpdmVyc2l0eSkKClNvbWV0aW1lcywgbG93IHF1YWxpdHkgY2VsbHMgbWF5IGVycm9uZW91c2x5IG1hcCB0byB0aGUgb3J0aG9jaHJvbWF0aWMgZXJ5dGhyb2JsYXN0IHJlZ2lvbiBhcyB0aGlzIGNlbGwgdHlwZSBoYXMgdmVyeSBsb3cgdHJhbnNjcmlwdGlvbmFsIGRpdmVyc2l0eS4gClRoZXNlIGxvdyBxdWFsaXR5IHF1ZXJ5IGNlbGxzIGRvIG5vdCBoYXZlIGhlbW9nbG9iaW4gZXhwcmVzc2lvbiBhbmQgYXJlIGluIGZhY3QgbWlzLW1hcHBlZDsgdGhleSB3aWxsIGJlIGZsYWdnZWQgYnkgdGhlIFFDIGZpbHRlciBhbmQgZXhjbHVkZWQgZnJvbSBjZWxsIHR5cGUgYXNzaWdubWVudHMuCgoqKlBsZWFzZSBhZGp1c3QgdGhlIE1BRF90aHJlc2hvbGQgKHR5cGljYWxseSBiZXR3ZWVuIDEgYW5kIDMpIGJhc2VkIG9uIHRoZSBkaXN0cmlidXRpb24gb2YgeW91ciBkYXRhc2V0IHRvIGlkZW50aWZ5IHRoZSBvdXRsaWVycyB3aXRoIGxvdyBxdWFsaXR5IGFuZCBoaWdoIG1hcHBpbmcgZXJyb3Igc2NvcmVzLiBUaGlzIHdpbGwgaW1wcm92ZSB5b3VyIGNsYXNzaWZpY2F0aW9ucyBhbmQgYW55IGRvd25zdHJlYW0gY29tcG9zaXRpb24gYW5hbHlzaXMqKgoKCmBgYHtyfQojICMgT3B0aW9uYWwgc3RlcCAtIHJlbW92ZSBvdXRsaWVycyB3aXRoIGhpZ2ggbWFwcGluZyBlcnJvcgojIHF1ZXJ5IDwtIHN1YnNldChxdWVyeSwgbWFwcGluZ19lcnJvcl9RQyA9PSAnUGFzcycpCmBgYAoKT3B0aW9uYWxseSwgb3V0bGllciBjZWxscyB3aXRoIGhpZ2ggbWFwcGluZyBlcnJvciBjYW4gYWxzbyBiZSByZW1vdmVkIGF0IHRoaXMgc3RhZ2UuCkZvciBlYXNlIG9mIGludGVncmF0aW5nIHRoZXNlIG1hcHBlZCBhbm5vdGF0aW9ucyB3aXRoIHRoZSByZXN0IG9mIHlvdXIgYW5hbHlzaXMsIHdlIGNhbiBjaG9vc2UgdG8gc2tpcCB0aGlzIHN0ZXAuIElmIHNvLCBGaW5hbCBDZWxsVHlwZSBhbmQgUHNldWRvdGltZSBwcmVkaWN0aW9ucyB3aWxsIGJlIGFzc2lnbmVkIGFzIE5BIGZvciBjZWxscyBmYWlsaW5nIHRoZSBtYXBwaW5nIGVycm9yIFFDIHRocmVzaG9sZC4gCgoKIyMjIENlbGwgVHlwZSBBc3NpZ25tZW50cwpXZSB3aWxsIG5leHQgdXNlIGEgS05OIGNsYXNzaWZpZXIgdG8gYXNzaWduIGNlbGwgaWRlbnRpdHkgYmFzZWQgb24gdGhlIDMwIEstTmVhcmVzdCBOZWlnaGJvdXJzIGZyb20gdGhlIHJlZmVyZW5jZSBtYXAuClRoaXMgbGFiZWwgdHJhbnNmZXIgc3RlcCB3aWxsIHRha2UgbG9uZ2VyLCBwb3RlbnRpYWxseSBhcm91bmQgMTAgbWludXRlcyBmb3IgfjEwLDAwMCBjZWxscyAKCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0KIyBQcmVkaWN0IEhlbWF0b3BvaWV0aWMgQ2VsbCBUeXBlcyBieSBLTk4gY2xhc3NpZmljYXRpb24KcXVlcnkgPC0gcHJlZGljdF9DZWxsVHlwZXMoCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIHJlZl9vYmogPSBCTV9yZWYsIAogIGluaXRpYWxfbGFiZWwgPSAnaW5pdGlhbF9DZWxsVHlwZV9Cb25lTWFycm93TWFwJywgIyBjZWxsdHlwZSBhc3NpZ25tZW50cyBiZWZvcmUgZmlsdGVyaW5nIG9uIG1hcHBpbmcgUUMKICBmaW5hbF9sYWJlbCA9ICdwcmVkaWN0ZWRfQ2VsbFR5cGVfQm9uZU1hcnJvd01hcCcgICMgY2VsbHR5cGUgYXNzaWdubWVudHMgd2l0aCBtYXAgUUMgZmFpbGluZyBjZWxscyBhc3NpZ25lZCBhcyBOQQopIAoKRGltUGxvdChzdWJzZXQocXVlcnksIG1hcHBpbmdfZXJyb3JfUUMgPT0gJ1Bhc3MnKSwgcmVkdWN0aW9uID0gJ3VtYXAnLCBncm91cC5ieSA9IGMoJ3ByZWRpY3RlZF9DZWxsVHlwZV9Cb25lTWFycm93TWFwJyksIAogICAgICAgIHJhc3Rlcj1GQUxTRSwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZSA9IDQpCmBgYAoKCiMjIyMgUHNldWRvdGltZSBBbm5vdGF0aW9ucwpXZSBjYW4gYWxzbyBhbm5vdGF0ZSBlYWNoIHF1ZXJ5IGNlbGwgYmFzZWQgb24gdGhlaXIgcG9zaXRpb24gYWxvbmcgaGVtYXRvcG9pZXRpYyBwc2V1ZG90aW1lLiAKUXVlcnkgY2VsbHMgd2lsbCBiZSBhc3NpZ25lZCBhIHBzZXVkb3RpbWUgc2NvcmUgYmFzZWQgb24gdGhlIDMwIEstTmVhcmVzdCBOZWlnaGJvdXJzIGZyb20gdGhlIHJlZmVyZW5jZSBtYXAuClNpbmNlIG91ciBQc2V1ZG90aW1lIEtOTiBhc3NpZ25tZW50cyBhcmUgcGVyZm9ybWVkIGluIFVNQVAgc3BhY2UgKG1vcmUgYWNjdXJhdGUgdGhhbiBLTk4gb24gaGFybW9ueSBjb21wb25lbnRzKSwgdGhpcyBzdGVwIGlzIHZlcnkgZmFzdCAoPCAxMHMpCgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMn0KIyBQcmVkaWN0IFBzZXVkb3RpbWUgdmFsdWVzIGJ5IEtOTgpxdWVyeSA8LSBwcmVkaWN0X1BzZXVkb3RpbWUoCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIHJlZl9vYmogPSBCTV9yZWYsIAogIGluaXRpYWxfbGFiZWwgPSAnaW5pdGlhbF9Qc2V1ZG90aW1lJywgICMgcHNldWRvdGltZSBhc3NpZ25tZW50cyBiZWZvcmUgZmlsdGVyaW5nIG9uIG1hcHBpbmcgUUMKICBmaW5hbF9sYWJlbCA9ICdwcmVkaWN0ZWRfUHNldWRvdGltZScgICAjIHBzZXVkb3RpbWUgYXNzaWdubWVudHMgd2l0aCBtYXAgUUMgZmFpbGluZyBjZWxscyBhc3NpZ25lZCBhcyBOQQopCgojIFZpc3VhbGl6ZSBIZW1hdG9wb2lldGljIFBzZXVkb3RpbWUgaW4gcXVlcnkgZGF0YQpGZWF0dXJlUGxvdChzdWJzZXQocXVlcnksIG1hcHBpbmdfZXJyb3JfUUMgPT0gJ1Bhc3MnKSwgZmVhdHVyZXMgPSBjKCdwcmVkaWN0ZWRfUHNldWRvdGltZScpLCBzcGxpdC5ieSA9ICdTYW1wbGUnKSAmIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSByZXYoYnJld2VyLnBhbCgxMSwgJ1JkQnUnKSkpCmBgYAoKIyMjIFZpc3VhbGl6ZSBQcm9qZWN0aW9uIERlbnNpdHkKCk5vdyBsZXQncyB2aXN1YWxpemUgdGhlIGRlbnNpdHkgZGlzdHJpYnV0aW9uIG9mIHF1ZXJ5IGNlbGxzIGFjcm9zcyB0aGUgaGVtYXRvcG9pZXRpYyBoaWVyYXJjaHkKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD04fQojIFNldCBiYXRjaC9jb25kaXRpb24gdG8gYmUgdmlzdWFsaXplZCBpbmRpdmlkdWFsbHkKYmF0Y2hfa2V5IDwtICdTYW1wbGUnCgojIHJldHVybnMgYSBsaXN0IG9mIHBsb3RzIGZvciBlYWNoIGRvbm9yIGZyb20gYSBwcmUtc3BlY2lmaWVkIGJhdGNoIHZhcmlhYmxlCnByb2plY3Rpb25fcGxvdHMgPC0gcGxvdF9Qcm9qZWN0aW9uX2J5RG9ub3IoCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIGJhdGNoX2tleSA9IGJhdGNoX2tleSwgCiAgcmVmX29iaiA9IEJNX3JlZiwgCiAgSGllcmFyY2h5X29ubHkgPSBGQUxTRSwgIyBXaGV0aGVyIHRvIGV4Y2x1ZGUgVC9OSy9QbGFzbWEvU3Ryb21hbCBjZWxscyAKICBkb3duc2FtcGxlX3JlZmVyZW5jZSA9IFRSVUUsIAogIGRvd25zYW1wbGVfZnJhYyA9IDAuMjUsICAgIyBkb3duLXNhbXBsZSByZWZlcmVuY2UgY2VsbHMgdG8gMjUlOyByZWR1Y2VzIGZpZ3VyZSBmaWxlIHNpemUKICBxdWVyeV9wb2ludF9zaXplID0gMC4yLCAgICMgYWRqdXN0IHNpemUgb2YgcXVlcnkgY2VsbHMgYmFzZWQgb24gIyBvZiBjZWxscwogIHNhdmVwbG90ID0gVFJVRSwgCiAgc2F2ZV9mb2xkZXIgPSAncHJvamVjdGlvbkZpZ3VyZXMvJwopCgojIHNob3cgcGxvdHMgdG9nZXRoZXIgd2l0aCBwYXRjaHdvcmsuIENhbiBhbHNvIGp1c3QgY2FsbCAicHJvamVjdGlvbl9wbG90cyIgb2JqZWN0IHRvIGRpc3BsYXkgb25lLWJ5LW9uZQpwYXRjaHdvcms6OndyYXBfcGxvdHMocHJvamVjdGlvbl9wbG90cywgbmNvbCA9IDIpCmBgYAoKV2UgY2FuIGFsc28gc2V0IEhpZXJhcmNoeV9vbmx5ID0gVFJVRSB0byByZW1vdmUgVC9OSy9QbGFzbWEvU3Ryb21hbCBjZWxscyBhbmQgZm9jdXMgc29sZWx5IG9uIHRoZSBoZW1hdG9wb2lldGljIGhpZXJhcmNoeS4KCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LjV9CiMgU2V0IGJhdGNoL2NvbmRpdGlvbiB0byBiZSB2aXN1YWxpemVkIGluZGl2aWR1YWxseQpiYXRjaF9rZXkgPC0gJ1NhbXBsZScKCiMgcmV0dXJucyBhIGxpc3Qgb2YgcGxvdHMgZm9yIGVhY2ggZG9ub3IgZnJvbSBhIHByZS1zcGVjaWZpZWQgYmF0Y2ggdmFyaWFibGUKcHJvamVjdGlvbl9wbG90cyA8LSBwbG90X1Byb2plY3Rpb25fYnlEb25vcigKICBxdWVyeV9vYmogPSBxdWVyeSwgCiAgYmF0Y2hfa2V5ID0gYmF0Y2hfa2V5LCAKICByZWZfb2JqID0gQk1fcmVmLCAKICBIaWVyYXJjaHlfb25seSA9IFRSVUUsICMgV2hldGhlciB0byBleGNsdWRlIFQvTksvUGxhc21hL1N0cm9tYWwgY2VsbHMgCiAgZG93bnNhbXBsZV9yZWZlcmVuY2UgPSBUUlVFLCAKICBkb3duc2FtcGxlX2ZyYWMgPSAwLjI1LCAgICMgZG93bi1zYW1wbGUgcmVmZXJlbmNlIGNlbGxzIHRvIDI1JTsgcmVkdWNlcyBmaWd1cmUgZmlsZSBzaXplCiAgcXVlcnlfcG9pbnRfc2l6ZSA9IDAuMiwgICAjIGFkanVzdCBzaXplIG9mIHF1ZXJ5IGNlbGxzIGJhc2VkIG9uICMgb2YgY2VsbHMKICBzYXZlcGxvdCA9IFRSVUUsIAogIHNhdmVfZm9sZGVyID0gJ3Byb2plY3Rpb25GaWd1cmVzLycKKQoKIyBzaG93IHBsb3RzIHRvZ2V0aGVyIHdpdGggcGF0Y2h3b3JrLiBDYW4gYWxzbyBqdXN0IGNhbGwgInByb2plY3Rpb25fcGxvdHMiIG9iamVjdCB0byBkaXNwbGF5IG9uZS1ieS1vbmUKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHByb2plY3Rpb25fcGxvdHMsIG5jb2wgPSAyKQpgYGAKCgpMZXQncyBzYXZlIHRoZSByZXN1bHRzIG9mIHRoZSBtYXBwaW5nIG9udG8gdGhlIGNvbXBsZXRlIGhlbWF0b3BvaWV0aWMgaGllcmFyY2h5LiAKCmBgYHtyfQpzYXZlUkRTKHF1ZXJ5LCAnUXVlcnlEYXRhX01hcHBlZF9Db21wbGV0ZUhlbWF0b3BvaWVzaXMucmRzJykKYGBgCgoKIyMjIyBCIENFTEwgREVWRUxPUE1FTlQgLSBEb3dubG9hZCByZWZlcmVuY2Ugb2JqZWN0IGFuZCBVTUFQIG1vZGVsCgpTZXQgcHJvamVjdGlvbiBmb2xkZXIsIERvd25sb2FkIHJlZmVyZW5jZSBvYmplY3QgYW5kIFVNQVAgbW9kZWwKCmBgYHtyfQojIFNldCBkaXJlY3RvcnkgdG8gc3RvcmUgcHJvamVjdGlvbiByZWZlcmVuY2UgZmlsZXMKcHJvamVjdGlvbl9wYXRoID0gJy4vJwoKIyBEb3dubG9hZCBCb25lIE1hcnJvdyBSZWZlcmVuY2UgLSAxODcgTWIKY3VybDo6Y3VybF9kb3dubG9hZCgnaHR0cHM6Ly9iZGV2ZWxvcG1lbnRtYXAuczMudXMtZWFzdC0yLmFtYXpvbmF3cy5jb20vQkRldmVsb3BtZW50X1JlZk1hcF9TeW1waG9ueVJlZi5yZHMnLAogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gcGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JEZXZlbG9wbWVudF9SZWZNYXBfU3ltcGhvbnlSZWYucmRzJykpCiMgRG93bmxvYWQgdXdvdCBtb2RlbCBmaWxlIC0gOTkgTWIKY3VybDo6Y3VybF9kb3dubG9hZCgnaHR0cHM6Ly9iZGV2ZWxvcG1lbnRtYXAuczMudXMtZWFzdC0yLmFtYXpvbmF3cy5jb20vQkRldmVsb3BtZW50X1JlZk1hcF91d290X21vZGVsLnV3b3QnLAogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gcGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JEZXZlbG9wbWVudF9SZWZNYXBfdXdvdF9tb2RlbC51d290JykpCgojIExvYWQgU3ltcGhvbnkgcmVmZXJlbmNlCmJkZXZfcmVmIDwtIHJlYWRSRFMocGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JEZXZlbG9wbWVudF9SZWZNYXBfU3ltcGhvbnlSZWYucmRzJykpCmJkZXZfcmVmJHVtYXAkZW1iZWRkaW5nWywxXSA9IC1iZGV2X3JlZiR1bWFwJGVtYmVkZGluZ1ssMV0gCgojIFNldCB1d290IHBhdGggZm9yIFVNQVAgcHJvamVjdGlvbgpiZGV2X3JlZiRzYXZlX3V3b3RfcGF0aCA8LSBwYXN0ZTAocHJvamVjdGlvbl9wYXRoLCAnQkRldmVsb3BtZW50X1JlZk1hcF91d290X21vZGVsLnV3b3QnKQpgYGAKCgojIyMjIFZpc3VhbGl6ZSBCb25lIE1hcnJvdyBSZWZlcmVuY2UKCklmIHdlIHdhbnQgdG8gdmlzdWFsaXplIGNlbGx0eXBlIGxhYmVscyBvciBtZXRhZGF0YSBmcm9tIHRoZSBCTSBSZWZlcmVuY2UsIHdlIGNhbiBjcmVhdGUgYSBTZXVyYXQgT2JqZWN0IGZyb20gdGhlIHN5bXBob255IHJlZmVyZW5jZSAKVGhpcyB3aWxsIGJlIG1lbW9yeSBlZmZpY2llbnQgYXMgaXQgd2lsbCBub3QgaW5jbHVkZSBnZW5lIGV4cHJlc3Npb24gY291bnRzLCBvbmx5IHRoZSBVTUFQIGNvb3JkaW5hdGVzIGFuZCB0aGUgbWV0YWRhdGEgaW5jbHVkaW5nIGNlbGwgbGFiZWxzIGFuZCBzb3J0aW5nIGluZm9ybWF0aW9uCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTF9CmJkZXZfcmVmX29iaiA8LSBjcmVhdGVfUmVmZXJlbmNlT2JqZWN0KGJkZXZfcmVmKQoKRGltUGxvdChiZGV2X3JlZl9vYmosIHJlZHVjdGlvbiA9ICd1bWFwJywgZ3JvdXAuYnkgPSAnQkRldmVsb3BtZW50X0NlbGxUeXBlJywgcmFzdGVyPUZBTFNFLCBsYWJlbD1UUlVFLCBsYWJlbC5zaXplID0gNCkKYGBgCgoKIyMjIE1hcHBpbmcgcXVlcnkgZGF0YSBvbnRvIEIgY2VsbCBkZXZlbG9wbWVudAoKYGBge3J9CnF1ZXJ5IDwtIHJlYWRSRFMoJ1F1ZXJ5RGF0YV9NYXBwZWRfQ29tcGxldGVIZW1hdG9wb2llc2lzLnJkcycpCnF1ZXJ5CmBgYAoKRmlsdGVyIGZvciBjZWxsdHlwZXMgYWxvbmcgQiBjZWxsIGRldmVsb3BtZW50CgpgYGB7cn0KQl9kZXZlbG9wbWVudF9jZWxsdHlwZXMgPC0gYygnSFNDJywgJ0hTQy9NUFAnLCAnTVBQLU15THknLCAnTVBQLUxNUFAnLCAnTE1QUCcsICdFYXJseSBHTVAnLCAnTUxQJywgJ01MUC1JSScsICdQcmUtcERDJywgJ1ByZS1wREMgQ3ljbGluZycsICdwREMnLCAKICAgICAgICAgICAgICAgICAgICAnQ0xQJywgJ0Vhcmx5UHJvQicsICdQcmUtUHJvQicsICdQcm8tQiBWREonLCAnUHJvLUIgQ3ljbGluZycsICdMYXJnZSBQcmUtQicsICdTbWFsbCBQcmUtQicsICdJbW1hdHVyZSBCJywgJ01hdHVyZSBCJykKCnF1ZXJ5IDwtIHN1YnNldChxdWVyeSwgcHJlZGljdGVkX0NlbGxUeXBlX0JvbmVNYXJyb3dNYXAgJWluJSBCX2RldmVsb3BtZW50X2NlbGx0eXBlcykKcXVlcnkKYGBgCgojIyMgTWFwIHRoZSBRdWVyeSBEYXRhClByb3ZpZGUgcmF3IGNvdW50cywgbWV0YWRhdGEsIGFuZCBkb25vciBrZXkuIFRoaXMgc2hvdWxkIHRha2UgPDEgbWluCkNhbGN1bGF0ZSBtYXBwaW5nIGVycm9yIGFuZCBwZXJmb3JtIFFDIHRvIHJlbW92ZSBsb3cgcXVhbGl0eSBjZWxscyB3aXRoIGhpZ2ggbWFwcGluZyBlcnJvcgoKYGBge3J9CiMgYmF0Y2ggdmFyaWFibGUgdG8gY29ycmVjdCBpbiB0aGUgcXVlcnkgZGF0YSwgc2V0IGFzIE5VTEwgaWYgbm8gYmF0Y2hlcyBpbiBxdWVyeQpiYXRjaHZhciA8LSAnU2FtcGxlJwoKIyBNYXAgcXVlcnkgZGF0YXNldCB1c2luZyBTeW1waG9ueSAoS2FuZyBldCBhbCAyMDIxKQpxdWVyeSA8LSBtYXBfUXVlcnkoCiAgICBleHBfcXVlcnkgPSBxdWVyeUBhc3NheXMkUk5BQGNvdW50cywgCiAgICBtZXRhZGF0YV9xdWVyeSA9IHF1ZXJ5QG1ldGEuZGF0YSwKICAgIHJlZl9vYmogPSBiZGV2X3JlZiwKICAgIHZhcnMgPSBiYXRjaHZhcgopCgojIEZsaXAgVU1BUDEgdG8gZ28gZnJvbSBsZWZ0IHRvIHJpZ2h0CnF1ZXJ5W1sndW1hcCddXUBjZWxsLmVtYmVkZGluZ3NbLDFdID0gLXF1ZXJ5W1sndW1hcCddXUBjZWxsLmVtYmVkZGluZ3NbLDFdCmBgYAoKYGBge3J9CnF1ZXJ5W1sndW1hcCddXQpgYGAKCgpgYGB7cn0KYmRldl9yZWYkbWV0YV9kYXRhCmBgYAoKCiMjIyBDZWxsIFR5cGUgQXNzaWdubWVudHMKV2Ugd2lsbCBuZXh0IHVzZSBhIEtOTiBjbGFzc2lmaWVyIHRvIGFzc2lnbiBjZWxsIGlkZW50aXR5IGJhc2VkIG9uIHRoZSAzMCBLLU5lYXJlc3QgTmVpZ2hib3VycyBmcm9tIHRoZSByZWZlcmVuY2UgbWFwLgpUaGlzIGxhYmVsIHRyYW5zZmVyIHN0ZXAgd2lsbCB0YWtlIGxvbmdlciwgcG90ZW50aWFsbHkgYXJvdW5kIDEwIG1pbnV0ZXMgZm9yIH4xMCwwMDAgY2VsbHMgCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CiMgUHJlZGljdCBIZW1hdG9wb2lldGljIENlbGwgVHlwZXMgYnkgS05OIGNsYXNzaWZpY2F0aW9uCnF1ZXJ5IDwtIHByZWRpY3RfQ2VsbFR5cGVzKAogIHF1ZXJ5X29iaiA9IHF1ZXJ5LCAKICByZWZfb2JqID0gYmRldl9yZWYsIAogIHJlZl9sYWJlbCA9ICdCRGV2ZWxvcG1lbnRfQ2VsbFR5cGUnLCAgICMjIGZvciBhIG1vcmUgZGV0YWlsZWQgYW5ub3RhdGlvbiwgdXNlIEJEZXZlbG9wbWVudF9DZWxsVHlwZV9Db21wcmVoZW5zaXZlCiAgaW5pdGlhbF9sYWJlbCA9ICdpbml0aWFsX0NlbGxUeXBlX0JEZXZlbG9wbWVudCcsICMgY2VsbHR5cGUgYXNzaWdubWVudHMgYmVmb3JlIGZpbHRlcmluZyBvbiBtYXBwaW5nIFFDCiAgZmluYWxfbGFiZWwgPSAncHJlZGljdGVkX0NlbGxUeXBlX0JEZXZlbG9wbWVudCcgICMgY2VsbHR5cGUgYXNzaWdubWVudHMgd2l0aCBtYXAgUUMgZmFpbGluZyBjZWxscyBhc3NpZ25lZCBhcyBOQQopIAoKRGltUGxvdChzdWJzZXQocXVlcnksIG1hcHBpbmdfZXJyb3JfUUMgPT0gJ1Bhc3MnKSwgcmVkdWN0aW9uID0gJ3VtYXAnLCBncm91cC5ieSA9IGMoJ3ByZWRpY3RlZF9DZWxsVHlwZV9CRGV2ZWxvcG1lbnQnKSwgCiAgICAgICAgcmFzdGVyPUZBTFNFLCBsYWJlbD1UUlVFLCBsYWJlbC5zaXplID0gNCkKYGBgCgoKIyMjIFZpc3VhbGl6ZSBQcm9qZWN0aW9uIERlbnNpdHkKCk5vdyBsZXQncyB2aXN1YWxpemUgdGhlIGRlbnNpdHkgZGlzdHJpYnV0aW9uIG9mIHF1ZXJ5IGNlbGxzIGFjcm9zcyB0aGUgaGVtYXRvcG9pZXRpYyBoaWVyYXJjaHkKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD04fQojIFNldCBiYXRjaC9jb25kaXRpb24gdG8gYmUgdmlzdWFsaXplZCBpbmRpdmlkdWFsbHkKYmF0Y2hfa2V5IDwtICdTYW1wbGUnCgojIHJldHVybnMgYSBsaXN0IG9mIHBsb3RzIGZvciBlYWNoIGRvbm9yIGZyb20gYSBwcmUtc3BlY2lmaWVkIGJhdGNoIHZhcmlhYmxlCnByb2plY3Rpb25fcGxvdHNfYmRldiA8LSBwbG90X1Byb2plY3Rpb25fYnlEb25vcigKICBxdWVyeV9vYmogPSBxdWVyeSwgCiAgYmF0Y2hfa2V5ID0gYmF0Y2hfa2V5LCAKICByZWZfb2JqID0gYmRldl9yZWYsIAogIGRvd25zYW1wbGVfcmVmZXJlbmNlID0gVFJVRSwgCiAgZG93bnNhbXBsZV9mcmFjID0gMC4yNSwgICAjIGRvd24tc2FtcGxlIHJlZmVyZW5jZSBjZWxscyB0byAyNSU7IHJlZHVjZXMgZmlndXJlIGZpbGUgc2l6ZQogIHF1ZXJ5X3BvaW50X3NpemUgPSAwLjIsICAgIyBhZGp1c3Qgc2l6ZSBvZiBxdWVyeSBjZWxscyBiYXNlZCBvbiAjIG9mIGNlbGxzCiAgc2F2ZXBsb3QgPSBUUlVFLCAKICBzYXZlX2ZvbGRlciA9ICdwcm9qZWN0aW9uRmlndXJlcy8nCikKCiMgc2hvdyBwbG90cyB0b2dldGhlciB3aXRoIHBhdGNod29yay4gQ2FuIGFsc28ganVzdCBjYWxsICJwcm9qZWN0aW9uX3Bsb3RzIiBvYmplY3QgdG8gZGlzcGxheSBvbmUtYnktb25lCnBhdGNod29yazo6d3JhcF9wbG90cyhwcm9qZWN0aW9uX3Bsb3RzX2JkZXYsIG5jb2wgPSAyKQpgYGAKCgpgYGB7cn0Kc2F2ZVJEUyhxdWVyeSwgJ1F1ZXJ5RGF0YV9NYXBwZWRfQmNlbGxEZXZlbG9wbWVudC5yZHMnKQpgYGAKCgoKCgoK